//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//	CSaveFile manager class
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#ifndef SAVEFILE_HPP
#define SAVEFILE_HPP

#include "types.hpp"
#include "hash/hash.hpp"
#include "compression/xfile.hpp"

//----------------------------------------------------------------------------
//	forward references
//----------------------------------------------------------------------------
typedef struct s_saverecord SSaveRecord;
class CSaveFile;
class CSaveObject;

typedef enum
{
	SAVEERR_OK					= 0,
	SAVEERR_BUSY				= -10000,
	SAVEERR_FILENOTFOUND,
	SAVEERR_CANNOTCREATEFILE,
	SAVEERR_IOERROR,
	SAVEERR_WRONGMODE,
	SAVEERR_MEMORYBLOCKMISMATCH,
	SAVEERR_IODISABLED,
	SAVEERR_OLDVERSION

} ESaveError;

//----------------------------------------------------------------------------
//	types of savable data
//----------------------------------------------------------------------------
typedef enum
{
	// these are all built-in types

	ST_EndOfTemplate=0,
	ST_ClassName,
	ST_Skip,
	ST_Passive,
	ST_Pointer,
	ST_SaveObject,
	ST_Class,

	// these are all extended types

	ST_ListOfObjects,
	ST_ListOfPointers,

} ESaveType;

//----------------------------------------------------------------------------
//	class types - used with SAVEDEF
//----------------------------------------------------------------------------
#define SCN_Normal		(0)
#define SCN_Partial		(1)
#define SCN_Virtual		(4)

//----------------------------------------------------------------------------
//	macros for creating SSaveRecord's
//----------------------------------------------------------------------------
#define membersize(obj,member)	sizeof(((obj*)0)->member)

#define SAVEDEF(obj,type)\
	{	ST_ClassName, type, 0, sizeof(obj), 0, "CLASS("#obj")"	}

#define SAVEDATA(obj,member)\
	{	ST_Passive, offsetof(obj,member),\
		1, membersize(obj,member), 0, "DATA("#obj"::"#member")"	}

#define SAVEPTR(obj,member)\
	{	ST_Pointer, offsetof(obj,member),\
		1, membersize(obj,member), 0, "PTR("#obj"::"#member")"	}

#define SAVEPOBJ(obj,member)\
	{	ST_SaveObject, offsetof(obj,member),\
		1, membersize(obj,member), 0, "POBJ("#obj"::"#member")"	}

#define SAVEOLIST(obj,listName)\
	{	ST_ListOfObjects,offsetof(obj,listName),\
		1, membersize(obj,listName), 0, "OBJLIST("#obj"::"#listName")"	}

#define SAVEPLIST(obj,listName)\
	{	ST_ListOfPointers,offsetof(obj,listName),\
		1, membersize(obj,listName), 0, "PTRLIST("#obj"::"#listName")"	}

#define SAVEBASE(baseClassName,baseTemplateName)\
	{	ST_Class, 0, 1, sizeof(baseClassName),\
		baseTemplateName, "BASE("#baseClassName")"	}

#define SAVESTRUCT(obj,structName,structTemplate)\
	{	ST_Class, offsetof(obj,structName), 1, membersize(obj,structName),\
		structTemplate, "STRUCT("#obj"::"#structName")"	}

#define DONTSAVE(obj,structName)\
	{	ST_Skip, offsetof(obj,structName),\
		1, membersize(obj,structName), 0, "SKIP("#obj"::"#structName")"	}

#define SAVEEND()\
	{	ST_EndOfTemplate, 0, 0, 0, 0, 0	}

//----------------------------------------------------------------------------
//	SSaveRecord definition
//----------------------------------------------------------------------------
typedef struct s_saverecord
{
	ESaveType			type;		// type
	u32					offset;		// offset
	u32					count;		// count of items
	u32					length;		// length of ONE item
	const SSaveRecord	*pTemplate;	// pointer to template for RECORD
	const char			*desc;		// description of record

} SSaveRecord;

//----------------------------------------------------------------------------
//	a memory block
//----------------------------------------------------------------------------
typedef struct
{
	const void	*ptr;
	u32			length;

} SAVEBLOCK;

//----------------------------------------------------------------------------
//	the CSaveFile class
//----------------------------------------------------------------------------
class CSaveFile
{
public:
					CSaveFile();
	virtual			~CSaveFile();

	inline bool		IsSaving()			{	return pSave!=0;	}
	inline bool		IsLoading()			{	return pLoad!=0;	}

	//-----------------------------------------------
	// get the save file version
	//-----------------------------------------------
	HASH			GetSaveVersion()	{	return saveVersion;	}

	//-----------------------------------------------
	// begin a save file session
	//-----------------------------------------------
	ESaveError		Begin( const char *filename, bool loading = false );

	//-----------------------------------------------
	// end a save file session
	//-----------------------------------------------
	ESaveError		End( ESaveError returnThisError=SAVEERR_OK );

	//-----------------------------------------------
	// reading and writing data to the file
	//-----------------------------------------------
	ESaveError		GetData( void *pData, u32 length );
	ESaveError		PutData( const void *pData, u32 length );

	template<class X> inline ESaveError GetData( X &data )
	{	return GetData(&data,sizeof(X));	}

	template<class X> inline ESaveError PutData( const X &data )
	{	return PutData(&data,sizeof(X));	}

	//-----------------------------------------------
	// declaring memory blocks
	//-----------------------------------------------
	u32				DeclareMemoryBlock( const void *pBlock, u32 length );

	//-----------------------------------------------
	// declaring savable objects
	//-----------------------------------------------
	u32				DeclareSaveObject( CSaveObject *pObj );

	//-----------------------------------------------
	// process the declared objects
	//-----------------------------------------------
	ESaveError		ProcessObjects();

	//-----------------------------------------------
	// manipulate templates
	//-----------------------------------------------
	void			PreSaveTemplate( const void *base, const SSaveRecord *pTemplate );
	void			SaveTemplate( const void *base, const SSaveRecord *pTemplate );
	void			PreLoadTemplate( void *base, const SSaveRecord *pTemplate );
	void			LoadTemplate( void *base, const SSaveRecord *pTemplate );

	//-----------------------------------------------
	// create an object from a give saveID
	// This must be implemented on a per game basis
	//-----------------------------------------------
	virtual CSaveObject	*CreateObject( u32 saveID ) = 0;

	//-----------------------------------------------
	// get the text for the error code
	//-----------------------------------------------
	const char		*GetErrorText( ESaveError err );

protected:

	//-----------------------------------------------
	//	manipulate individual records
	//	override these functions to add new savable types
	//-----------------------------------------------
	virtual void	PreSaveRecord( const void *base, ESaveType type, u32 length );
	virtual void	SaveRecord( const void *base, ESaveType type, u32 length );
	virtual void	PreLoadRecord( void *base, ESaveType type, u32 length );
	virtual void	LoadRecord( void *base, ESaveType type, u32 length );

	//-----------------------------------------------
	// auto-detecting object changes
	// also handles save game version #
	//-----------------------------------------------
	//	VerifyTemplates should be called from the
	//	derived class's constructor to get a list
	//	of missing data elements when the game
	//	boots. If this is not done during construction,
	//	it is done when initializing the first transaction.
	//-----------------------------------------------
	bool			VerifyTemplates();
	bool			VerifyTemplate( const SSaveRecord *pTemplate );
	bool			ReportUnsavedData( const SSaveRecord *classDef, u32 length, u32 offset );
	virtual void	Report( const char *errmsg ) = 0;

	//-----------------------------------------------
	// disable IO during presave and preload
	//-----------------------------------------------
	void			DisableIO( bool disable=true )	{	disableIO = disable;	}
	bool			IOEnabled()						{	return !disableIO;	}

	//-----------------------------------------------
	// reset the save system
	//-----------------------------------------------
	void			Reset( bool closeFile=true );

	//-----------------------------------------------
	// declare the memory block for an object
	//-----------------------------------------------
	u32				DeclareMemoryBlock( const CSaveObject *pObj );

	//-----------------------------------------------
	// create a hash for the memory block
	//-----------------------------------------------
	HASH			CreateMemoryBlockHash();

	//-----------------------------------------------
	// find which memory block a pointer is in
	//-----------------------------------------------
	u32				FindMemoryBlock( const void *forPtr, u32 &offset );
	const void		*GetPointer( u32 id, u32 offset );

	//-----------------------------------------------
	// find an object's ID
	//-----------------------------------------------
	u32				FindSaveObject( CSaveObject *pObj );
	CSaveObject		*GetSaveObject( u32 id );

protected:
	XFILEREAD	*pLoad;
	XFILEWRITE	*pSave;

	bool		disableIO;
	HASH		saveVersion;

	u32			maxBlocks;
	u32			maxObjects;
	u32			numBlocks;
	u32			numObjects;
	SAVEBLOCK	*blockList;
	CSaveObject	**objectList;

	// TODO: define this array, that keeps a list
	// of all the valid SSaveRecord's 
	static const SSaveRecord	*templateList[];

};

//----------------------------------------------------------------------------
//	a savable object
//----------------------------------------------------------------------------
class CSaveObject
{
public:
	virtual const SSaveRecord	*GetSaveTemplate() const = 0;
	virtual u32					GetSaveID() const	{	return 0;	}
	
	virtual void PreSave( CSaveFile *pFile ) const
	{
		pFile->PreSaveTemplate(this,GetSaveTemplate());
	}

	virtual void Save( CSaveFile *pFile ) const
	{
		pFile->SaveTemplate(this,GetSaveTemplate());
	}

	virtual void PreLoad( CSaveFile *pFile )
	{
		pFile->PreLoadTemplate(this,GetSaveTemplate());
	}

	virtual void Load( CSaveFile *pFile )
	{
		pFile->LoadTemplate(this,GetSaveTemplate());
	}
};


#endif
